package com.express.gateway.verticle;

import com.express.gateway.common.CommonCons;
import com.express.gateway.common.DataStoreConstant;
import com.express.gateway.common.EventBusTopicConst;
import com.express.gateway.common.util.StrUtil;
import com.express.gateway.manage.entity.TrackInfo;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;

import static com.express.gateway.common.CommonCons.KEY_CLUSTER;

/**
 * 一些系统参数
 *
 * @author flower
 */
public class MetricsTraceVerticle extends AbstractVerticle {
    private static final Logger LOG = LogManager.getLogger(MetricsTraceVerticle.class);

    /**
     * Exp-Inlet的启动时间
     */
    private final LocalDateTime apiStartTime = LocalDateTime.now();
    /**
     * 线程数量
     */
    private int availableProcessors = 0;
    /**
     * JVM总的内存量
     */
    private long totalMemory = 0;
    /**
     * JVM的空闲内存量
     */
    private long freeMemory = 0;
    /**
     * JVM最大内存量
     */
    private long maxMemory = 0;
    /**
     * 异常次数
     */
    private int errorCount = 0;
    /**
     * 请求到达的次数
     */
    private long requestApiCount = 0;
    /**
     * 请求到达核心处理器(HTTP/HTTPS)的次数
     */
    private long requestHttpApiCount = 0;
    /**
     * 核心处理器(HTTP/HTTPS)当前正在处理API的数量
     */
    private long currentHttpApiProcessingCount = 0;

    /**
     * 存储API的监控记录信息
     */
    private final Map<String, Deque<JsonObject>> trackSucceededMap = new HashMap<>();
    /**
     * 存储API请求失败的数数
     */
    private final Map<String, Long> reqFailedCount = new HashMap<>();
    /**
     * 存储API请求的数量
     */
    private final Map<String, Long> reqCount = new HashMap<>();

    @Override
    public void start(Promise<Void> startFuture) throws Exception {
        LOG.info("start Trace Verticle");
        vertx.eventBus().consumer(EventBusTopicConst.SYSTEM_GET_INFO, this::getSysInfo);
        vertx.eventBus().consumer(EventBusTopicConst.SYSTEM_PLUS_ERROR, this::PlusError);
        vertx.eventBus().consumer(EventBusTopicConst.SYSTEM_PLUS_TRACK_INFO, this::plusTrackInfo);
        vertx.eventBus().consumer(EventBusTopicConst.SYSTEM_GET_TRACK_INFO, this::getTrackInfo);
        vertx.eventBus().consumer(EventBusTopicConst.SYSTEM_BLACK_IP_FIND, this::findIpList);
        vertx.eventBus().consumer(EventBusTopicConst.SYSTEM_BLACK_IP_REPLACE, this::replaceIpList);
        vertx.eventBus().consumer(EventBusTopicConst.SYSTEM_PLUS_EXP_REQUEST, msg -> requestApiCount++);
        vertx.eventBus().consumer(EventBusTopicConst.SYSTEM_PLUS_HTTP_API_REQUEST, msg -> {
            requestHttpApiCount++;
            currentHttpApiProcessingCount++;
        });
        vertx.eventBus().consumer(EventBusTopicConst.SYSTEM_MINUS_CURRENT_PROCESSING, msg -> {
            if (currentHttpApiProcessingCount > 0) {
                currentHttpApiProcessingCount--;
            }
        });

        LOG.info("start Trace Verticle successful");
        super.start(startFuture);
    }

    /**
     * 查看系统基本信息
     *
     * @param msg
     */
    public void getSysInfo(Message<JsonObject> msg) {
        // 获取在线网关应用与API的数量
        Promise<JsonObject> countResult = Promise.promise();
        vertx.eventBus().<JsonObject>request(EventBusTopicConst.DEPLOY_APP_COUNT, null, res -> {
            if (res.failed()) {
                countResult.complete(new JsonObject());
                LOG.error("执行获取在线应用的数量 失败:", res.cause());
                return;
            }
            JsonObject result = res.result().body();
            LOG.debug("执行获取在线应用的数量 结果:{}", result);
            countResult.complete(result);
        });
        countResult.future().onComplete(res -> {
            availableProcessors = Runtime.getRuntime().availableProcessors();
            totalMemory = Runtime.getRuntime().totalMemory();
            freeMemory = Runtime.getRuntime().freeMemory();
            maxMemory = Runtime.getRuntime().maxMemory();
            JsonObject result = new JsonObject();
            result.put("availableProcessors", availableProcessors);
            result.put("totalMemory", totalMemory / (1024 * 1024));
            result.put("freeMemory", freeMemory / (1024 * 1024));
            result.put("maxMemory", maxMemory / (1024 * 1024));
            Duration duration = Duration.between(apiStartTime, LocalDateTime.now());
            result.put("expApiRunTime", StrUtil.millisToDateTime(duration.toMillis(), "$dD $hH $mMin $sS"));
            result.put("appCount", res.result().getInteger("app", 0));
            result.put("apiCount", res.result().getInteger("api", 0));
            result.put("errorCount", errorCount);
            result.put("requestApiCount", requestApiCount);
            result.put("requestHttpApiCount", requestHttpApiCount);
            result.put("currentHttpApiProcessingCount", currentHttpApiProcessingCount);
            vertx.eventBus().<JsonObject>request(EventBusTopicConst.FIND_BLACKLIST, null, blackList -> {
                if (res.succeeded()) {
                    JsonObject body = blackList.result().body();
                    if (body.getValue(DataStoreConstant.BLACKLIST_CONTENT_NAME) instanceof JsonArray) {
                        result.put("content", body.getJsonArray(DataStoreConstant.BLACKLIST_CONTENT_NAME));
                    } else if (body.getValue(DataStoreConstant.BLACKLIST_CONTENT_NAME) instanceof String) {
                        result.put("content", new JsonArray(body.getString(DataStoreConstant.BLACKLIST_CONTENT_NAME)));
                    } else {
                        result.put("content", new JsonArray());
                    }
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("执行查询运行状态 结果:" + result);
                    }
                    msg.reply(result);
                } else {
                    LOG.error("执行查询运行状态 结果:" + blackList.cause());
                    msg.fail(500, blackList.cause().getMessage());
                }
            });
        });
    }

    /**
     * 添加异常的数量
     *
     * @param msg
     */
    public void PlusError(Message<JsonObject> msg) {
        errorCount += 1;
        if (Objects.isNull(msg.body())) {
            return;
        }
        TrackInfo track = TrackInfo.fromJson(msg.body());
        LOG.error("应用:{} , API:{} ,在执行的过程中发生了异常:{}", track.getAppName(), track.getApiName(), track.getErrMsg());
    }

    /**
     * 添加API追踪信息
     *
     * @param msg
     */
    public void plusTrackInfo(Message<JsonObject> msg) {
        if (Objects.isNull(msg.body())) {
            return;
        }
        TrackInfo track = TrackInfo.fromJson(msg.body());
        LOG.debug("应用:{} , API:{} ,执行结果{}", track.getAppName(), track.getApiName(), track);
        // map的key
        String key = track.getAppName() + "-" + track.getApiName();
        // 记录API相关信息
        if (!track.isSuccessful()) {
            // 记录异常
            errorCount += 1;
            reqFailedCount.putIfAbsent(key, 0L);
            reqFailedCount.put(key, reqFailedCount.get(key) + 1);
            LOG.error("应用:{} , API:{} ,在执行的过程中发生了异常:{}", track.getAppName(), track.getApiName(), track.getErrMsg());
        } else {
            JsonObject json = new JsonObject();
            Duration proc = Duration.between(track.getStartTime(), track.getEndTime());
            json.put("time", track.getStartTime());
            json.put("overallTime", proc.toMillis());
            Duration reqs = Duration.between(track.getRequestTime(), track.getResponseTime());
            json.put("requestTime", reqs.toMillis());
            json.put("requestBodyLen", track.getRequestBufferLen());
            json.put("responseBodyLen", track.getResponseBufferLen());
            if (trackSucceededMap.get(key) == null) {
                trackSucceededMap.put(key, new LinkedList<>());
            } else {
                if (trackSucceededMap.get(key).size() > 100) {
                    trackSucceededMap.get(key).pollFirst();
                }
            }
            trackSucceededMap.get(key).add(json);
        }
        // 添加请求数量统计
        reqCount.putIfAbsent(key, 0L);
        reqCount.put(key, reqCount.get(key) + 1);
    }

    /**
     * 查看API运行信息
     *
     * @param msg
     */
    public void getTrackInfo(Message<JsonObject> msg) {
        String appName = msg.body().getString(CommonCons.KEY_APP_NAME);
        String apiName = msg.body().getString(CommonCons.KEY_API_NAME);
        String key = appName + "-" + apiName;
        JsonObject result = new JsonObject();
        result.put("rc", reqCount.get(key) == null ? 0L : reqCount.get(key));
        result.put("ec", reqFailedCount.get(key) == null ? 0L : reqFailedCount.get(key));
        result.put("track", trackSucceededMap.get(key) == null ? new JsonObject() : trackSucceededMap.get(key));
        msg.reply(result);
    }

    /**
     * 查看黑名单ip地址
     *
     * @param msg
     */
    public void findIpList(Message<JsonObject> msg) {
        vertx.eventBus().<JsonObject>request(EventBusTopicConst.FIND_BLACKLIST, null, res -> {
            if (res.failed()) {
                msg.fail(500, res.cause().getMessage());
                return;
            }
            JsonObject body = res.result().body();
            if (body.getValue(DataStoreConstant.BLACKLIST_CONTENT_NAME) instanceof JsonArray) {
                msg.reply(body.getJsonArray(DataStoreConstant.BLACKLIST_CONTENT_NAME));
            } else if (body.getValue(DataStoreConstant.BLACKLIST_CONTENT_NAME) instanceof String) {
                msg.reply(new JsonArray(body.getString(DataStoreConstant.BLACKLIST_CONTENT_NAME)));
            } else {
                msg.reply(new JsonArray());
            }
        });
    }

    /**
     * 更新黑名单ip地址
     *
     * @param msg
     */
    public void replaceIpList(Message<JsonObject> msg) {
        if (msg.body() == null) {
            msg.fail(1400, "参数为空或者缺少参数");
            return;
        }

        if (msg.body().getValue("ipList") instanceof JsonArray) {
            JsonArray array = msg.body().getJsonArray("ipList");
            JsonObject body = new JsonObject().put("blacklistName", "blacklist").put("blacklistBody", array);
            vertx.eventBus().<Integer>request(EventBusTopicConst.REPLACE_BLACKLIST, body, res -> {
                if (res.succeeded()) {
                    msg.reply(res.result().body());
                    // 广播更新自己ip地址
                    vertx.eventBus().publish(KEY_CLUSTER + EventBusTopicConst.SYSTEM_PUBLISH_BLACK_IP_LIST, array);
                } else {
                    msg.fail(500, res.cause().getMessage());
                }
            });
        } else {
            msg.fail(1405, "参数为空或者缺少参数");
        }
    }
}
